home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / SEARCH.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  21KB  |  1,014 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMitation
  4.  *
  5.  * Code Contributions By:    Bram Moolenaar            mool@oce.nl
  6.  *                            Tim Thompson            twitch!tjt
  7.  *                            Tony Andrews            onecom!wldrdg!tony
  8.  *                            G. R. (Fred) Walter     watmath!watcgl!grwalter
  9.  */
  10. /*
  11.  * search.c: code for normal mode searching commands
  12.  */
  13.  
  14. #include "vim.h"
  15. #include "globals.h"
  16. #include "proto.h"
  17. #include "param.h"
  18.  
  19. /* modified Henry Spencer's regular expression routines */
  20. #include "regexp.h"
  21.  
  22. static int inmacro __ARGS((char *, char *));
  23. static int cls __ARGS((void));
  24.  
  25. /*
  26.  * This file contains various searching-related routines. These fall into
  27.  * three groups:
  28.  * 1. string searches (for /, ?, n, and N)
  29.  * 2. character searches within a single line (for f, F, t, T, etc)
  30.  * 3. "other" kinds of searches like the '%' command, and 'word' searches.
  31.  */
  32.  
  33. /*
  34.  * String searches
  35.  *
  36.  * The string search functions are divided into two levels:
  37.  * lowest:    searchit(); called by dosearch() and docmdline().
  38.  * Highest: dosearch(); changes Curpos, called by normal().
  39.  *
  40.  * The last search pattern is remembered for repeating the same search.
  41.  * This pattern is shared between the :g, :s, ? and / commands.
  42.  * This is in myregcomp().
  43.  *
  44.  * The actual string matching is done using a heavily modified version of
  45.  * Henry Spencer's regular expression library.
  46.  *
  47.  */
  48.  
  49. static char     *search_pattern = NULL;  /* previous search pattern */
  50. static int        want_start;              /* looking for start of line? */
  51.  
  52. /*
  53.  * translate search pattern for regcomp()
  54.  */
  55.     regexp *
  56. myregcomp(pat)
  57.     char *pat;
  58. {
  59.     regexp *retval;
  60.  
  61.     if (pat == NULL || *pat == NUL)     /* use previous search pattern */
  62.     {
  63.         if (search_pattern == NULL)
  64.         {
  65.             emsg(e_noprevre);
  66.             return (regexp *) NULL;
  67.         }
  68.         pat = search_pattern;
  69.     }
  70.     else
  71.     {
  72.         if (search_pattern != NULL)
  73.             free(search_pattern);
  74.         search_pattern = strsave(pat);
  75.         reg_magic = p_magic;        /* Magic sticks with the r.e. */
  76.     }
  77.     want_start = (*pat == '^');        /* looking for start of line? */
  78.     reg_ic = p_ic;                /* tell the regexec routine how to search */
  79.     retval = regcomp(pat);
  80.     return retval;
  81. }
  82.  
  83. /*
  84.  * lowest level search function.
  85.  * Search for 'count'th occurrence of 'str' in direction 'dir'.
  86.  * Start at position 'pos' and return the found position in 'pos'.
  87.  * Return 1 for success, 0 for failure.
  88.  */
  89.     int
  90. searchit(pos, dir, str, count, end)
  91.     FPOS    *pos;
  92.     int     dir;
  93.     char    *str;
  94.     long    count;
  95.     int        end;
  96. {
  97.     int             found;
  98.     linenr_t        lnum;
  99.     linenr_t        startlnum;
  100.     regexp           *prog;
  101.     register char  *s;
  102.     char           *ptr;
  103.     register int    i;
  104.     register char  *match, *matchend;
  105.     int             loop;
  106.  
  107.     if ((prog = myregcomp(str)) == NULL)
  108.     {
  109.         emsg(e_invstring);
  110.         return 0;
  111.     }
  112. /*
  113.  * find the string
  114.  */
  115.     found = 1;
  116.     while (count-- && found)    /* stop after count matches, or no more matches */
  117.     {
  118.         startlnum = pos->lnum;    /* remember start of search for detecting no match */
  119.         found = 0;                /* default: not found */
  120.  
  121.         i = pos->col + dir;     /* search starts one postition away */
  122.         lnum = pos->lnum;
  123.  
  124.         if (dir == BACKWARD)
  125.         {
  126.             if (i < 0)
  127.                 --lnum;
  128.         }
  129.  
  130.         for (loop = 0; loop != 2; ++loop)   /* do this twice if 'wrapscan' is set */
  131.         {
  132.             for ( ; lnum > 0 && lnum <= line_count; lnum += dir, i = -1)
  133.             {
  134.                 s = ptr = nr2ptr(lnum);
  135.                 if (dir == FORWARD && i > 0)    /* first line for forward search */
  136.                 {
  137.                     if (want_start || strlen(s) <= i)   /* match not possible */
  138.                         continue;
  139.                     s += i;
  140.                 }
  141.  
  142.                 if (regexec(prog, s, dir == BACKWARD || i <= 0))
  143.                 {                            /* match somewhere on line */
  144.                     match = prog->startp[0];
  145.                     matchend = prog->endp[0];
  146.                     if (dir == BACKWARD && !want_start)
  147.                     {
  148.                         /*
  149.                          * Now, if there are multiple matches on this line, we have to
  150.                          * get the last one. Or the last one before the cursor, if we're
  151.                          * on that line.
  152.                          */
  153.                         while (regexec(prog, prog->startp[0] + 1, (int)FALSE))
  154.                         {
  155.                             if ((i >= 0) && ((prog->startp[0] - s) > i))
  156.                                 break;
  157.                             match = prog->startp[0];
  158.                             matchend = prog->endp[0];
  159.                         }
  160.  
  161.                         if ((i >= 0) && ((match - s) > i))
  162.                             continue;
  163.                     }
  164.  
  165.                     pos->lnum = lnum;
  166.                     if (end)
  167.                         pos->col = (int) (matchend - ptr - 1);
  168.                     else
  169.                         pos->col = (int) (match - ptr);
  170.                     found = 1;
  171.                     break;
  172.                 }
  173.                 /* breakcheck is slow, do it only once in 16 lines */
  174.                 if ((lnum & 15) == 0)
  175.                     breakcheck();       /* stop if ctrl-C typed */
  176.                 if (got_int)
  177.                     break;
  178.  
  179.                 if (loop && lnum == startlnum)  /* if second loop stop where started */
  180.                     break;
  181.             }
  182.     /* stop the search if wrapscan isn't set, after an interrupt and after a match */
  183.             if (!p_ws || got_int || found)
  184.                 break;
  185.  
  186.             if (dir == BACKWARD)    /* start second loop at the other end */
  187.                 lnum = line_count;
  188.             else
  189.                 lnum = 1;
  190.         }
  191.         if (got_int)
  192.             break;
  193.     }
  194.  
  195.     free((char *) prog);
  196.  
  197.     if (!found)             /* did not find it */
  198.     {
  199.         if (got_int)
  200.                 emsg(e_interr);
  201.         else
  202.                 emsg(e_patnotf);
  203.         return 0;
  204.     }
  205.  
  206.     return 1;
  207. }
  208.  
  209. /*
  210.  * Highest level string search function.
  211.  * Search for the 'count'th occurence of string 'str' in direction 'dir'
  212.  *                     If 'dir' is 0: use previous dir.
  213.  * If 'str' is 0 or 'str' is empty: use previous string.
  214.  *              If 'reverse' is TRUE: go in reverse of previous dir.
  215.  */
  216.     int
  217. dosearch(dir, str, reverse, count)
  218.     int                dir;
  219.     char           *str;
  220.     int                reverse;
  221.     long            count;
  222. {
  223.     FPOS            pos;        /* position of the last match */
  224.     int                dirc;
  225.     static int        lastsdir = FORWARD;    /* previous search direction */
  226.     static int        lastoffline;/* previous/current search has line offset */
  227.     static int        lastend;    /* previous/current search set cursor at end */
  228.     static long     lastoff;    /* previous/current line or char offset */
  229.     static int        nosetpm;    /* do not call setpcmark() */
  230.     register char    *p;
  231.     register long    c;
  232.     char            *dircp = NULL;
  233.  
  234.     if (dir == 0)
  235.         dir = lastsdir;
  236.     else
  237.         lastsdir = dir;
  238.     if (reverse)
  239.         dir = -dir;
  240.  
  241.     dirc = (dir == FORWARD ? '/' : '?');
  242.     if (str == NULL || *str == NUL)     /* use previous string and options */
  243.     {
  244.         if (search_pattern == NULL)
  245.         {
  246.             emsg(e_noprevre);
  247.             return 0;
  248.         }
  249.         str = "";
  250.     }
  251.     else
  252.     {
  253.         /* If there is a matching '/' or '?', toss it */
  254.         for (p = str; *p; ++p)
  255.         {
  256.             if (*p == dirc)
  257.             {
  258.                 dircp = p;        /* remember where we put the NUL */
  259.                 *p++ = NUL;
  260.                 break;
  261.             }
  262.             if (*p == '\\' && p[1] != NUL)
  263.                 ++p;    /* skip next character */
  264.         }
  265.  
  266.         lastoffline = FALSE;
  267.         lastend = FALSE;
  268.         nosetpm = FALSE;
  269.         lastoff = 0;
  270.         switch (*p)
  271.         {
  272.             case 'n':   nosetpm = TRUE; /* do not call setpcmark() */
  273.                         break;
  274.             case '+':
  275.             case '-':                   /* got a line offset */
  276.                         lastoffline = TRUE;
  277.                         break;
  278.             case 'e':                   /* position cursor at end */
  279.                         lastend = TRUE;
  280.             case 's':                   /* got a character offset */
  281.                         ++p;
  282.         }
  283.         if (*p == '+' || *p == '-')     /* got a line offset */
  284.             lastoff = atol(p);
  285.     }
  286.  
  287.     smsg("%c%s", dirc, *str == NUL ? search_pattern : str);
  288.     if (lastoffline || lastend || lastoff || nosetpm)
  289.     {
  290.         outchar(dirc);
  291.         if (nosetpm)
  292.             outchar('n');
  293.         else if (lastend)
  294.             outchar('e');
  295.         else if (!lastoffline)
  296.             outchar('s');
  297.         if (lastoff < 0)
  298.         {
  299.             outchar('-');
  300.             outnum((int)-lastoff);
  301.         }
  302.         else if (lastoff > 0 || lastoffline)
  303.         {
  304.             outchar('+');
  305.             outnum((int)lastoff);
  306.         }
  307.     }
  308.  
  309.     gotocmdline(FALSE, NUL);
  310.     flushbuf();
  311.  
  312.     pos = Curpos;
  313.  
  314.     c = searchit(&pos, dir, str, count, lastend);
  315.     if (dircp)
  316.         *dircp = dirc;        /* put second '/' or '?' back for normal() */
  317.     if (!c)
  318.         return 0;
  319.  
  320.     if (!lastoffline)           /* add the character offset to the column */
  321.     {
  322.         if (lastoff > 0)        /* offset to the right, check for end of line */
  323.         {
  324.             p = pos2ptr(&pos) + 1;
  325.             c = lastoff;
  326.             while (c-- && *p++ != NUL)
  327.                 ++pos.col;
  328.         }
  329.         else                    /* offset to the left, check for start of line */
  330.         {
  331.             if ((c = pos.col + lastoff) < 0)
  332.                 c = 0;
  333.             pos.col = c;
  334.         }
  335.     }
  336.  
  337.     if (!nosetpm)
  338.         setpcmark();
  339.     Curpos = pos;
  340.     set_want_col = TRUE;
  341.  
  342.     if (!lastoffline)
  343.         return 1;
  344.  
  345. /*
  346.  * add the offset to the line number.
  347.  */
  348.     c = Curpos.lnum + lastoff;
  349.     if (c < 1)
  350.         Curpos.lnum = 1;
  351.     else if (c > line_count)
  352.         Curpos.lnum = line_count;
  353.     else
  354.         Curpos.lnum = c;
  355.     Curpos.col = 0;
  356.  
  357.     return 2;
  358. }
  359.  
  360.  
  361. /*
  362.  * Character Searches
  363.  */
  364.  
  365. /*
  366.  * searchc(c, dir, type, count)
  367.  *
  368.  * Search for character 'c', in direction 'dir'. If 'type' is 0, move to the
  369.  * position of the character, otherwise move to just before the char.
  370.  * Repeat this 'count' times.
  371.  */
  372.     int
  373. searchc(c, dir, type, count)
  374.     int             c;
  375.     register int    dir;
  376.     int             type;
  377.     long            count;
  378. {
  379.     static char     lastc = NUL;    /* last character searched for */
  380.     static int        lastcdir;        /* last direction of character search */
  381.     static int        lastctype;        /* last type of search ("find" or "to") */
  382.     register int    col;
  383.     char            *p;
  384.     int             len;
  385.  
  386.     if (c != NUL)       /* normal search: remember args for repeat */
  387.     {
  388.         lastc = c;
  389.         lastcdir = dir;
  390.         lastctype = type;
  391.     }
  392.     else                /* repeat previous search */
  393.     {
  394.         if (lastc == NUL)
  395.             return FALSE;
  396.         if (dir)        /* repeat in opposite direction */
  397.             dir = -lastcdir;
  398.         else
  399.             dir = lastcdir;
  400.     }
  401.  
  402.     p = nr2ptr(Curpos.lnum);
  403.     col = Curpos.col;
  404.     len = strlen(p);
  405.  
  406.     /*
  407.      * On 'to' searches, skip one to start with so we can repeat searches in
  408.      * the same direction and have it work right.
  409.      * REMOVED to get vi compatibility
  410.      * if (lastctype)
  411.      *    col += dir;
  412.      */
  413.  
  414.     while (count--)
  415.     {
  416.             for (;;)
  417.             {
  418.                 if ((col += dir) < 0 || col >= len)
  419.                     return FALSE;
  420.                 if (p[col] == lastc)
  421.                         break;
  422.             }
  423.     }
  424.     if (lastctype)
  425.         col -= dir;
  426.     Curpos.col = col;
  427.     return TRUE;
  428. }
  429.  
  430. /*
  431.  * "Other" Searches
  432.  */
  433.  
  434. /*
  435.  * showmatch - move the cursor to the matching paren or brace
  436.  */
  437.     FPOS           *
  438. showmatch()
  439. {
  440.     static FPOS pos;
  441.     char        initc;        /* char under cursor */
  442.     char        findc;        /* matching char */
  443.     char        c;
  444.     int         count = 0;
  445.     int         i;
  446.     static char table[6] = {'(', ')', '[', ']', '{', '}'};
  447.     int         inquote = 0;
  448.     register char        *p;
  449.  
  450.     pos = Curpos;
  451.  
  452.     for (p = nr2ptr(pos.lnum); ;++pos.col)
  453.     {
  454.         if ((initc = p[pos.col]) == NUL)
  455.             return (FPOS *) NULL;
  456.         for (i = 0; i < 6; ++i)
  457.             if (table[i] == initc)
  458.                 goto foundit;
  459.     }
  460.  
  461. foundit:
  462.     if (i & 1)                  /* backward search */
  463.         findc = table[i - 1];
  464.     else                        /* forward search */
  465.         findc = table[i + 1];
  466.     i &= 1;
  467.  
  468.     /* we only do a breakcheck() once for every 16 lines */
  469.     while (!got_int)
  470.     {
  471.             /* we could use inc() and dec() here, but that is much slower */
  472.         if (i)              /* backward search */
  473.         {
  474.             if (pos.col == 0)   /* at start of line, go to previous one */
  475.             {
  476.                 if (pos.lnum == 1)      /* start of file */
  477.                     break;
  478.                 --pos.lnum;
  479.                 p = nr2ptr(pos.lnum);
  480.                 pos.col = strlen(p);
  481.                 if ((pos.lnum & 15) == 0)
  482.                     breakcheck();
  483.             }
  484.             else
  485.                 --pos.col;
  486.         }
  487.         else                    /* forward search */
  488.         {
  489.             if (p[pos.col] == NUL)  /* at end of line, go to next one */
  490.             {
  491.                 if (pos.lnum == line_count) /* end of file */
  492.                     break;
  493.                 ++pos.lnum;
  494.                 pos.col = 0;
  495.                 p = nr2ptr(pos.lnum);
  496.                 if ((pos.lnum & 15) == 0)
  497.                     breakcheck();
  498.             }
  499.             else
  500.                 ++pos.col;
  501.         }
  502.  
  503.         /*
  504.          * anything that is preceded with a backslash is ignored
  505.          */
  506.         if (pos.col == 0 || p[pos.col - 1] != '\\')
  507.         {
  508.         /*
  509.          * Things inside quotes are ignored by setting 'inquote'.
  510.          * If we find a quote without a preceding '\' invert 'inquote'.
  511.          * At the end of a line not ending in '\' we reset 'inquote'.
  512.          */
  513.             switch (c = p[pos.col])
  514.             {
  515.             case NUL:
  516.                 inquote = FALSE;
  517.                 break;
  518.  
  519.             case '"':
  520.                 inquote = !inquote;
  521.                 break;
  522.  
  523.             /*
  524.              * Skip things in single quotes: 'x' or '\x'.
  525.              * Be careful for single single quotes, eg jon's.
  526.              * Things like '\233' or '\x3f' are ok, there is no brace in it.
  527.              */
  528.             case '\'':
  529.                 if (i)                        /* backward search */
  530.                 {
  531.                     if (pos.col > 1)
  532.                     {
  533.                         if (p[pos.col - 2] == '\'')
  534.                             pos.col -= 2;
  535.                         else if (p[pos.col - 2] == '\\' && pos.col > 2 && p[pos.col - 3] == '\'')
  536.                             pos.col -= 3;
  537.                     }
  538.                 }
  539.                 else if (p[pos.col + 1])    /* forward search */
  540.                 {
  541.                     if (p[pos.col + 1] == '\\' && p[pos.col + 2] && p[pos.col + 3] == '\'')
  542.                         pos.col += 3;
  543.                     else if (p[pos.col + 2] == '\'')
  544.                         pos.col += 2;
  545.                 }
  546.                 break;
  547.  
  548.             default:
  549.                 if (!inquote)      /* only check for match outside of quotes */
  550.                 {
  551.                     if (c == initc)
  552.                         count++;
  553.                     else if (c == findc)
  554.                     {
  555.                         if (count == 0)
  556.                             return &pos;
  557.                         count--;
  558.                     }
  559.                 }
  560.             }
  561.         }
  562.     }
  563.     return (FPOS *) NULL;       /* never found it */
  564. }
  565.  
  566. /*
  567.  * findfunc(dir, what) - Find the next line starting with 'what' in direction 'dir'
  568.  *
  569.  * Return TRUE if a line was found.
  570.  */
  571.     int
  572. findfunc(dir, what, count)
  573.     int         dir;
  574.     int            what;
  575.     long        count;
  576. {
  577.     linenr_t    curr;
  578.  
  579.     curr = Curpos.lnum;
  580.  
  581.     for (;;)
  582.     {
  583.         if (dir == FORWARD)
  584.         {
  585.                 if (curr++ == line_count)
  586.                         break;
  587.         }
  588.         else
  589.         {
  590.                 if (curr-- == 1)
  591.                         break;
  592.         }
  593.  
  594.         if (*nr2ptr(curr) == what)
  595.         {
  596.             if (--count > 0)
  597.                 continue;
  598.             setpcmark();
  599.             Curpos.lnum = curr;
  600.             Curpos.col = 0;
  601.             return TRUE;
  602.         }
  603.     }
  604.  
  605.     return FALSE;
  606. }
  607.  
  608. /*
  609.  * findsent(dir, count) - Find the start of the next sentence in direction 'dir'
  610.  * Sentences are supposed to end in ".", "!" or "?" followed by white space,
  611.  * or at an empty line.
  612.  * Return TRUE if the next sentence was found.
  613.  */
  614.     int
  615. findsent(dir, count)
  616.         int     dir;
  617.         long    count;
  618. {
  619.         FPOS            pos, tpos;
  620.         register int    c;
  621.         int             (*func) __PARMS((FPOS *));
  622.         int             startlnum;
  623.  
  624.         pos = Curpos;
  625.         if (dir == FORWARD)
  626.                 func = incl;
  627.         else
  628.                 func = decl;
  629.  
  630.         while (count--)
  631.         {
  632.                 /* if on an empty line, skip upto a non-empty line */
  633.                 if (gchar(&pos) == NUL)
  634.                 {
  635.                         do
  636.                                 if ((*func)(&pos) == -1)
  637.                                         break;
  638.                         while (gchar(&pos) == NUL);
  639.                         if (dir == FORWARD)
  640.                                 goto found;
  641.                 }
  642.                 /* if on the start of a paragraph or a section and searching
  643.                  * forward, go to the next line */
  644.                 else if (dir == FORWARD && pos.col == 0 && startPS(pos.lnum, NUL))
  645.                 {
  646.                         if (pos.lnum == line_count)
  647.                             return FALSE;
  648.                         ++pos.lnum;
  649.                         goto found;
  650.                 }
  651.                 else if (dir == BACKWARD)
  652.                         decl(&pos);
  653.  
  654.                 /* go back to the previous non-blank char */
  655.                 while ((c = gchar(&pos)) == ' ' || c == '\t' ||
  656.                         (dir == BACKWARD && strchr(".!?)]\"'", c) != NULL))
  657.                         if (decl(&pos) == -1)
  658.                                 break;
  659.  
  660.                 /* remember the line where the search started */
  661.                 startlnum = pos.lnum;
  662.  
  663.                 for (;;)                /* find end of sentence */
  664.                 {
  665.                         if ((c = gchar(&pos)) == NUL ||
  666.                                 (pos.col == 0 && startPS(pos.lnum, NUL)))
  667.                         {
  668.                             if (dir == BACKWARD && pos.lnum != startlnum)
  669.                                 ++pos.lnum;
  670.                             break;
  671.                         }
  672.                         if (c == '.' || c == '!' || c == '?')
  673.                         {
  674.                                 tpos = pos;
  675.                                 do
  676.                                     if ((c = inc(&tpos)) == -1)
  677.                                         break;
  678.                                 while (strchr(")}\"'", c = gchar(&tpos)) != NULL);
  679.                                 if (c == -1  || c == ' ' || c == '\t' || c == NUL)
  680.                                 {
  681.                                         pos = tpos;
  682.                                         if (gchar(&pos) == NUL) /* skip NUL at EOL */
  683.                                                 inc(&pos);
  684.                                         break;
  685.                                 }
  686.                         }
  687.                         if ((*func)(&pos) == -1)
  688.                         {
  689.                                 if (count)
  690.                                         return FALSE;
  691.                                 break;
  692.                         }
  693.                 }
  694. found:
  695.                 /* skip white space */
  696.                 while ((c = gchar(&pos)) == ' ' || c == '\t')
  697.                         if (incl(&pos) == -1)
  698.                                 break;
  699.         }
  700.  
  701.         Curpos = pos;
  702.         setpcmark();
  703.         return TRUE;
  704. }
  705.  
  706. /*
  707.  * findpar(dir, count, what) - Find the next paragraph in direction 'dir'
  708.  * Paragraphs are currently supposed to be separated by empty lines.
  709.  * Return TRUE if the next paragraph was found.
  710.  * If 'what' is '{' or '}' we go to the next section.
  711.  */
  712.     int
  713. findpar(dir, count, what)
  714.     register int    dir;
  715.     long            count;
  716.     int             what;
  717. {
  718.     register linenr_t    curr;
  719.     int                    did_skip;        /* TRUE after separating lines have
  720.                                                 been skipped */
  721.     int                    first;            /* TRUE on first line */
  722.  
  723.     curr = Curpos.lnum;
  724.  
  725.     while (count--)
  726.     {
  727.         did_skip = FALSE;
  728.         for (first = TRUE; ; first = FALSE)
  729.         {
  730.                 if (*nr2ptr(curr) != NUL)
  731.                     did_skip = TRUE;
  732.  
  733.                 if (!first && did_skip && startPS(curr, what))
  734.                     break;
  735.  
  736.                 if ((curr += dir) < 1 || curr > line_count)
  737.                 {
  738.                         if (count)
  739.                                 return FALSE;
  740.                         curr -= dir;
  741.                         break;
  742.                 }
  743.         }
  744.     }
  745.     setpcmark();
  746.     Curpos.lnum = curr;
  747.     if (curr == line_count)
  748.     {
  749.         if ((Curpos.col = strlen(nr2ptr(curr))) != 0)
  750.                 --Curpos.col;
  751.     }
  752.     else
  753.         Curpos.col = 0;
  754.     return TRUE;
  755. }
  756.  
  757. /*
  758.  * check if the string 's' is a nroff macro that is in option 'opt'
  759.  */
  760.     static int
  761. inmacro(opt, s)
  762.         char *opt;
  763.         register char *s;
  764. {
  765.         register char *macro;
  766.  
  767.         for (macro = opt; macro[0]; ++macro)
  768.         {
  769.                 if (macro[0] == s[0] && (((s[1] == NUL || s[1] == ' ')
  770.                         && (macro[1] == NUL || macro[1] == ' ')) || macro[1] == s[1]))
  771.                         break;
  772.                 ++macro;
  773.                 if (macro[0] == NUL)
  774.                         break;
  775.         }
  776.         return (macro[0] != NUL);
  777. }
  778.  
  779. /*
  780.  * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
  781.  * If 'para' is '{' or '}' only check for sections.
  782.  */
  783.     int
  784. startPS(lnum, para)
  785.     linenr_t    lnum;
  786.     int         para;
  787. {
  788.     register char *s;
  789.  
  790.     s = nr2ptr(lnum);
  791.     if ((para == NUL && *s == '{') || *s == para || *s == '\f')
  792.         return TRUE;
  793.     if (*s++ != '.')
  794.         return FALSE;
  795.     if (inmacro(p_sections, s) || (!para && inmacro(p_para, s)))
  796.         return TRUE;
  797.     else
  798.         return FALSE;
  799. }
  800.  
  801. /*
  802.  * The following routines do the word searches performed by the 'w', 'W',
  803.  * 'b', 'B', 'e', and 'E' commands.
  804.  */
  805.  
  806. /*
  807.  * To perform these searches, characters are placed into one of three
  808.  * classes, and transitions between classes determine word boundaries.
  809.  *
  810.  * The classes are:
  811.  *
  812.  * 0 - white space
  813.  * 1 - letters, digits and underscore
  814.  * 2 - everything else
  815.  */
  816.  
  817. static int        stype;            /* type of the word motion being performed */
  818.  
  819. /*
  820.  * cls() - returns the class of character at Curpos
  821.  *
  822.  * The 'type' of the current search modifies the classes of characters if a 'W',
  823.  * 'B', or 'E' motion is being done. In this case, chars. from class 2 are
  824.  * reported as class 1 since only white space boundaries are of interest.
  825.  */
  826.     static int
  827. cls()
  828. {
  829.     register int c;
  830.  
  831.     c = gcharCurpos();
  832.     if (c == ' ' || c == '\t' || c == NUL)
  833.         return 0;
  834.  
  835.     if (isidchar(c))
  836.         return 1;
  837.  
  838.     /*
  839.      * If stype is non-zero, report these as class 1.
  840.      */
  841.     return (stype == 0) ? 2 : 1;
  842. }
  843.  
  844.  
  845. /*
  846.  * fwd_word(count, type) - move forward one word
  847.  *
  848.  * Returns TRUE if end of the file was reached.
  849.  */
  850.     int
  851. fwd_word(count, type)
  852.     long        count;
  853.     int         type;
  854. {
  855.     int         sclass;     /* starting class */
  856.  
  857.     stype = type;
  858.     while (--count >= 0)
  859.     {
  860.         sclass = cls();
  861.  
  862.         /*
  863.          * We always move at least one character.
  864.          */
  865.         if (incCurpos() == -1)
  866.             return TRUE;
  867.  
  868.         if (sclass != 0)
  869.             if (skip_chars(sclass, FORWARD))
  870.                 return TRUE;
  871.  
  872.         /*
  873.          * go to next non-white
  874.          */
  875.         while (cls() == 0)
  876.         {
  877.             /*
  878.              * We'll stop if we land on a blank line
  879.              */
  880.             if (Curpos.col == 0 && *nr2ptr(Curpos.lnum) == NUL)
  881.                 break;
  882.  
  883.             if (incCurpos() == -1)
  884.                 return TRUE;
  885.         }
  886.     }
  887.     return FALSE;
  888. }
  889.  
  890. /*
  891.  * bck_word(count, type) - move backward 'count' words
  892.  *
  893.  * Returns TRUE if top of the file was reached.
  894.  */
  895.     int
  896. bck_word(count, type)
  897.     long        count;
  898.     int         type;
  899. {
  900.     int         sclass;     /* starting class */
  901.  
  902.     stype = type;
  903.     while (--count >= 0)
  904.     {
  905.         sclass = cls();
  906.  
  907.         if (decCurpos() == -1)
  908.             return TRUE;
  909.  
  910.         if (cls() != sclass || sclass == 0)
  911.         {
  912.             /*
  913.              * We were at the start of a word. Go back to the end of the prior
  914.              * word.
  915.              */
  916.             while (cls() == 0)  /* skip any white space */
  917.             {
  918.                 /*
  919.                  * We'll stop if we land on a blank line
  920.                  */
  921.                 if (Curpos.col == 0 && *nr2ptr(Curpos.lnum) == NUL)
  922.                     goto finished;
  923.  
  924.                 if (decCurpos() == -1)
  925.                     return TRUE;
  926.             }
  927.             sclass = cls();
  928.         }
  929.  
  930.         /*
  931.          * Move backward to start of this word.
  932.          */
  933.         if (skip_chars(sclass, BACKWARD))
  934.                 return TRUE;
  935.  
  936.         incCurpos();                    /* overshot - forward one */
  937. finished:
  938.         ;
  939.     }
  940.     return FALSE;
  941. }
  942.  
  943. /*
  944.  * end_word(count, type, stop) - move to the end of the word
  945.  *
  946.  * There is an apparent bug in the 'e' motion of the real vi. At least on the
  947.  * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
  948.  * motion crosses blank lines. When the real vi crosses a blank line in an
  949.  * 'e' motion, the cursor is placed on the FIRST character of the next
  950.  * non-blank line. The 'E' command, however, works correctly. Since this
  951.  * appears to be a bug, I have not duplicated it here.
  952.  *
  953.  * Returns TRUE if end of the file was reached.
  954.  *
  955.  * If stop is TRUE and we are already on the end of a word, move one less.
  956.  */
  957.     int
  958. end_word(count, type, stop)
  959.     long        count;
  960.     int         type;
  961.     int            stop;
  962. {
  963.     int         sclass;     /* starting class */
  964.  
  965.     stype = type;
  966.     while (--count >= 0)
  967.     {
  968.         sclass = cls();
  969.         if (incCurpos() == -1)
  970.             return TRUE;
  971.  
  972.         /*
  973.          * If we're in the middle of a word, we just have to move to the end of it.
  974.          */
  975.         if (cls() == sclass && sclass != 0)
  976.         {
  977.             /*
  978.              * Move forward to end of the current word
  979.              */
  980.             if (skip_chars(sclass, FORWARD))
  981.                     return TRUE;
  982.         }
  983.         else if (!stop || sclass == 0)
  984.         {
  985.             /*
  986.              * We were at the end of a word. Go to the end of the next word.
  987.              */
  988.  
  989.             if (skip_chars(0, FORWARD))     /* skip any white space */
  990.                 return TRUE;
  991.  
  992.             /*
  993.              * Move forward to the end of this word.
  994.              */
  995.             if (skip_chars(cls(), FORWARD))
  996.                 return TRUE;
  997.         }
  998.         decCurpos();                    /* overshot - backward one */
  999.         stop = FALSE;                    /* we move only one word less */
  1000.     }
  1001.     return FALSE;
  1002. }
  1003.  
  1004.     int
  1005. skip_chars(class, dir)
  1006.     int class;
  1007.     int dir;
  1008. {
  1009.         while (cls() == class)
  1010.             if ((dir == FORWARD ? incCurpos() : decCurpos()) == -1)
  1011.                 return 1;
  1012.         return 0;
  1013. }
  1014.